package com.auditlog.sql.parser;

import cn.hutool.core.map.WeakConcurrentMap;
import cn.hutool.core.text.AntPathMatcher;
import cn.hutool.core.util.StrUtil;
import com.auditlog.datasource.context.LogContext;
import com.auditlog.datasource.context.LogContextHolder;
import com.auditlog.datasource.db.SqlType;
import com.auditlog.datasource.struct.TableInfo;
import com.auditlog.datasource.table.extrator.TableExtractor;
import com.auditlog.datasource.table.extrator.TableExtractorHolder;
import com.auditlog.sql.factory.SqlRunnerFactory;
import com.auditlog.sql.factory.SqlRunnerFactoryRegistry;
import com.auditlog.datasource.struct.SqlMeta;
import com.auditlog.util.CollectionUtils;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.statement.Statement;
import net.sf.jsqlparser.statement.Statements;

import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * 解析sql，并缓存
 *
 * @author Zhiyang.Zhang
 * @version 1.0
 * @date 2023/9/23 18:58
 */
@Slf4j
public class CachedSqlParser {

    private static final Map<String, SqlMeta> SQL_META_MAP = new WeakConcurrentMap<>();

    private static final AntPathMatcher MATCHER_CASE_SENSITIVE = new AntPathMatcher();

    private static final AntPathMatcher MATCHER_ALL = new AntPathMatcher().setCaseSensitive(false);

    public static SqlMeta parse(String sql) {
        return CollectionUtils.computeIfAbsent(SQL_META_MAP, sql, v -> {
            List<Statement> statementList;
            SqlMeta sqlMeta = new SqlMeta();
            try {
                Statements statements = CCJSqlParserUtil.parseStatements(sql);
                statementList = statements.getStatements();
                sqlMeta.setStatements(statementList);
                for (Statement s : statementList) {
                    SqlRunnerFactory sqlRunnerFactory = SqlRunnerFactoryRegistry.getInstance().support(s);
                    if (sqlRunnerFactory == null) {
                        log.warn("不支持的sql:{}", sql);
                    }
                    SqlType sqlType = sqlRunnerFactory.sqlType();
                    if (sqlType != SqlType.ALL) {
                        TableExtractor extractor = TableExtractorHolder.extractor(s.getClass());
                        TableInfo tableInfo = extractor.extract(s);
                        String tableName = tableInfo.getName();
                        boolean ignore = ignore(tableName);
                        if (ignore) {
                            sqlType = SqlType.ALL;
                        }
                    }
                    sqlMeta.addSqlType(sqlType);
                }
            } catch (JSQLParserException e) {
                log.error("sql解析出现异常:{},", sql, e);
                sqlMeta.setSqlType(SqlType.EXCEPTION);
            }
            return sqlMeta;
        });
    }

    /**
     * 判断是否是需要忽略的table
     *
     * @param tableName
     * @return: boolean
     */
    public static boolean ignore(String tableName) {
        LogContext logContext = LogContextHolder.get();
        Set<String> ignoreTables = logContext.getIgnoreTables();
        boolean caseSensitive = logContext.isCaseSensitive();
        for (String ignoreTable : ignoreTables) {
            if (StrUtil.isBlank(ignoreTable)) {
                continue;
            }
            if (caseSensitive) {
                if(MATCHER_CASE_SENSITIVE.match(ignoreTable,tableName)){
                    return true;
                }
            } else {
                if(MATCHER_ALL.match(ignoreTable,tableName)){
                    return true;
                }
            }
        }
        return false;
    }
}
